package com.project.aaos.job;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.pab.framework.foundation.MaskUtils;
import com.project.aaos.config.Config;
import com.project.aaos.dao.ApproFileInfoMapper;
import com.project.aaos.dao.ApproLineInfoMapper;
import com.project.aaos.exception.AAOSException;
import com.project.aaos.pojo.*;
import com.project.aaos.util.AAOSUtil;
import com.project.aaos.util.FixedLenUtil;
import com.project.aaos.util.WholeToHalfUtil;
import org.apache.ibatis.session.SqlSession;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;

public class BusinessJob implements Job {

    private Logger log = LoggerFactory.getLogger(BusinessJob.class);

    private ApproFileInfoMapper approFileMapper;
    private ApproLineInfoMapper approLineMapper;

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        SqlSession session = (SqlSession) jobExecutionContext.getJobDetail().getJobDataMap().get("session");
        approFileMapper = session.getMapper(ApproFileInfoMapper.class);
        approLineMapper = session.getMapper(ApproLineInfoMapper.class);

        Config config = (Config) jobExecutionContext.getJobDetail().getJobDataMap().get("config");
        perform(config);

    }


    /*
     * 正常执行：查询状态为1的File，循环处理——>更改状态为3——>处理——>更改状态为4
     */
    public void perform(Config config) {

        //0.找到状态为1的file
        List<ApproFileInfo> list = approFileMapper.selectByState(1);
        if (list.size()==0){
            log.info("[ AAOS ] " +config.getBank()+ " (BusinessModule) " + "no data detected, task over");
            return;
        } else {
            log.info("[ AAOS ] " +config.getBank()+ " (BusinessModule) " + "new data detected, task start");
        }


        //对状态为1的file进行循环处理
        for (ApproFileInfo approFileInfo : list) {

            //初始化变量：文件id、文件名、结论文件名、结论文件绝对路径
            Integer id = approFileInfo.getId();
            String fileName = approFileInfo.getFileName();
            String resultFileName = "RS-" + fileName;
            String desc = config.getDescDir() + "/" + "RS-" + fileName;


            //1.更改状态为3标识开始处理
            approFileInfo.setState(3);
            approFileMapper.updateByPrimaryKey(approFileInfo);
            log.info("[ AAOS ] " +config.getBank()+ " (BusinessModule) " + fileName + " change record state to 3, task start");


            //2.写入文件
            File descFile = new File(desc);
            if (descFile.exists()){
                descFile.delete();
            }
            try {
                //由此进入处理文件核心逻辑
                writeFile(id, config, desc,fileName);
            } catch (IOException e) {
                log.error(e.getMessage());
                throw new AAOSException(e.getMessage());
            }
            log.info("[ AAOS ] " +config.getBank()+ " (BusinessModule) " + fileName + " write file over");


            //4.更改状态为4标识处理结束
            approFileInfo.setState(4);
            approFileInfo.setResultFileName(resultFileName);
            approFileMapper.updateByPrimaryKey(approFileInfo);
            //一个文件对应的记录量可能非常大，处理完一个文件提交一次事务
            log.info("[ AAOS ] " +config.getBank()+ " (BusinessModule) " + fileName + " change record state to 4,task over");

        }
        log.info("[ AAOS ] " +config.getBank()+ " (BusinessModule) " + " task over");
    }




    /*
     * 根据文件id查出文件，并将其中行转换为行方结论文件
     * 参数：fileId 文件id    config 配置   descFilePath 目标文件位置    fileName 文件名
     * fileName用于错误日志打印时定位到文件
     **/
    private void writeFile(Integer fileId, Config config, String descFilePath,String fileName) throws IOException {



        List<ApproLineInfo> list = approLineMapper.selectByFileId(fileId);

        String aaisFieldsPath = config.getFieldsDir()+"/aais-"+fileName;
        String errorMsgPath = config.getErrorDir()+"/error-"+fileName;
        String errorCommentPath = config.getErrorDir()+"/comment-"+fileName;

        OutputStream aaisFieldsOutputStream = new FileOutputStream(aaisFieldsPath);
        OutputStream errorMsgOutputStream = new FileOutputStream(errorMsgPath);
        OutputStream errorCommentOutputStream = new FileOutputStream(errorCommentPath);
        OutputStream resultFileOutputStream = new FileOutputStream(descFilePath);


        boolean flag1 = false;
        boolean flag2 = false;
        boolean flag3 = false;
        boolean flag4 = false;

        StringBuffer resultStringBuffer = new StringBuffer();

        //循环写入文件
        for (int i=0;i<list.size();i++){

            ApproLineInfo approLineInfo = list.get(i);
            //将审批结论文件加工成行方结论文件
            Map<String,String> map = lineHandler(approLineInfo, config,fileName,i+1);

            //获取并加工结论文件字符串，第一行只有内容，以后每一行前面都加一个换行符
            String resultStr = "";
            if (!map.get("resultStr").equals("empty")){
                resultStr = map.get("resultStr");
                if (flag1){
                    resultStr = config.getLineSeparator() + resultStr;
                }
                flag1 = true;
            }

            //获取错误字符串，第一行只有内容，以后每一行前面都加一个换行符
            String errorStr = "";
            if (!map.get("errStr").equals("empty")){
                errorStr = map.get("errStr");
                if (flag2){
                    errorStr = config.getLineSeparator() + errorStr;
                }
                flag2 = true;
            }


            //获取其他平台调用字符串
            String fieldsStr = "aais-->"+map.get("aais")+","+"acrs-->outAppNo:"+map.get("outAppNo");
            if (flag3){
                fieldsStr = config.getLineSeparator()+fieldsStr;
            }
            flag3 = true;

            //获取错误注释，第一行只有内容，以后每一行前面都加一个换行符
            String errorComment = "";
            if (!map.get("errComment").equals("empty")){
                errorComment = map.get("errComment");
                if (flag4){
                    errorComment = config.getLineSeparator() + errorComment;
                }
                flag4 = true;
            }

            resultStringBuffer.append(resultStr);

            aaisFieldsOutputStream.write(fieldsStr.getBytes(config.getDescCode()));
            resultFileOutputStream.write(resultStr.getBytes(config.getDescCode()));
            errorMsgOutputStream.write(errorStr.getBytes(config.getDescCode()));
            errorCommentOutputStream.write(errorComment.getBytes(config.getDescCode()));


        }

        //如果最后一行不是空行，在最后一行加换行符
        if (!config.getEndLineEmpty().equals("false")){
            resultFileOutputStream.write(config.getLineSeparator().getBytes(config.getDescCode()));
        }

        aaisFieldsOutputStream.close();
        resultFileOutputStream.close();
        errorMsgOutputStream.close();
        errorCommentOutputStream.close();

        File errorFile = new File(errorMsgPath);
        File commentFile = new File(errorCommentPath);
        if (errorFile.length() == 0){
            errorFile.delete();
            commentFile.delete();
        } else {
            new File(aaisFieldsPath.substring(0,aaisFieldsPath.lastIndexOf("."))+".ok").createNewFile();
            new File(errorMsgPath.substring(0,errorMsgPath.lastIndexOf("."))+".ok").createNewFile();
        }



        //如果结论文件为空,且源文件不为空 则删除，如果结论文件不为空则生成.ok文件
        if (resultStringBuffer.length()==0&&list.size()!=0){
            File resultFile = new File(descFilePath);
            resultFile.delete();
        } else {
            File resultOkFile = new File(descFilePath.substring(0,descFilePath.lastIndexOf("."))+".ok");
            resultOkFile.createNewFile();
        }


    }

    /*
     *  将ApproLineInfo转换为行方结论文件
     *  参数：lineInfos 审批结论文件行  config 配置    fileName 文件名  count 文件行 用于定位问题所在位置
     *  返回值：map 三个key:resultStr\errStr\errComment
     *  如果resultStr有值说明处理成功，errStr\errComment都是empty    如果resultStr是empty，则errStr\errComment都有值
     */
    private Map<String,String> lineHandler(ApproLineInfo lineInfos,Config config,String fileName,int count) {



        //获取approLineInfo对应的行记录
        String line = JSON.toJSONString(lineInfos);;


        //获取脱敏后行关键信息
        String identityNo = MaskUtils.maskIdentityNo(lineInfos.getGlobal_id());
        String mobile = MaskUtils.maskMobileNo(lineInfos.getMobile());
        String approMsg = "[application_no:"+lineInfos.getApplication_no()+",chinese_name:"+lineInfos.getChinese_name()+",mobile:"+mobile+",global_id:"+identityNo+"]";



        Map<String,String> map = new HashMap<>();
        JSONObject sourServiceData = new JSONObject();


        //调用发卡接入平台
        if ("true".equals(config.getAaisRemoteCall())){
            //1.1调用发卡接入平台，获取busiData信息
            sourServiceData = callAais(config,lineInfos,fileName,count);

            //1.2校验网申必输字段
            String aaisCheckResult = checkAaisFields(config,sourServiceData,fileName,count,approMsg);
            if (!aaisCheckResult.equals("empty")){

                if (config.getPassApprovalFields().equals("false")){
                    log.error(aaisCheckResult);
                    throw new AAOSException(aaisCheckResult);
                }

                map.put("resultStr","empty");
                map.put("errStr",line);
                map.put("errComment",aaisCheckResult);
                map.put("aais",sourServiceData.toString().replace("\n","").replace(" ",""));
                log.error(aaisCheckResult);
                return map;
            }
        }



        //2.调用征信平台
        if ("true".equals(config.getAcrsRemoteCall())){
            //调用征信平台，获取busiData信息
            JSONObject acrsServiceData = callAcrs(config,lineInfos,fileName,count);
            String outAppNo = "";
            if (acrsServiceData!=null){
                outAppNo = acrsServiceData.getString("outAppNo");
            }
            sourServiceData.put("outAppNo",outAppNo);
            map.put("acrs",outAppNo);
        }

        //3.校验审批必输字段
        String approvalCheckResult = checkApproFileds(config,lineInfos,fileName,count,approMsg,line);
        if (!approvalCheckResult.equals("empty")){

            if (config.getPassApprovalFields().equals("false")){
                log.error(approvalCheckResult);
                throw new AAOSException(approvalCheckResult);
            }
            map.put("resultStr","empty");
            map.put("errStr",line);
            map.put("errComment",approvalCheckResult);
            log.error(approvalCheckResult);
            return map;
        }

        //4.校验字段定长
        JSONObject serviceData = new JSONObject();
        //如果全角转半角开关打开那么执行全角转半角
        if (config.getWholeToHalf().equals("true")){
            lineInfos = WholeToHalfUtil.convertBean(lineInfos);
        }
        serviceData = config.getLineHandler().toServiceData(lineInfos,sourServiceData,serviceData);
        Map<String,String> resultMap = config.getLineHandler().toResultMap(approMsg,fileName,count,serviceData,config);

        if (resultMap.get("errComment").equals("empty")){

            map.put("resultStr",resultMap.get("resultStr"));
            map.put("errStr","empty");
            map.put("errComment","empty");
            return map;
        } else {

            String errComment = "[ AAOS ] FieldOverLengthError (BusinessModule) "+fileName + " row " + count +"fields:" + resultMap.get("errComment") +" over length "+approMsg;
            if (config.getPassOverLenthFields().equals("false")){
                log.error(errComment);
                throw new AAOSException(errComment);
            }

            map.put("resultStr","empty");
            map.put("errStr",line);
            map.put("errComment",errComment);
            log.error(errComment);
            return map;
        }
    }


    private JSONObject callAais(Config config,ApproLineInfo lineInfo,String fileName,int count){

        RequestInfo requestInfo = config.getRequestInfo();

        String appId = requestInfo.getSysHeader().getAppId();
        String orgCode = requestInfo.getSysHeader().getOrgCode();
        SysHeader sysHeader = new SysHeader(String.valueOf(new Date().getTime()), appId, UUID.randomUUID().toString().replace("-", ""), orgCode);

        String queryType = requestInfo.getBusiData().getQueryType();
        List<String> queryPara = requestInfo.getBusiData().getQueryPara();

        BusiData busiData = new BusiData(queryType, lineInfo.getApplication_no(), queryPara,lineInfo.getBar_code());

        //拼接请求参数
        RequestInfo requestInfoNew = new RequestInfo(sysHeader, busiData);

        JSONObject sourServiceData;
        try {
            sourServiceData = AAOSUtil.httpCall("http://127.0.0.1/aais/apply/applyInfo.do",requestInfoNew);
        } catch (Exception e){
            log.error("[ AAOS ] "+config.getBank()+" (BusinessModule) "+fileName+" row "+ count+" AAIS Remote Call ERROR",e);
            throw new AAOSException("[ AAOS ] "+config.getBank()+" (BusinessModule) "+fileName+" row "+ count+" AAIS Remote Call ERROR",e);
        }


        return sourServiceData.getJSONObject("busiData");
    }

    private JSONObject callAcrs(Config config,ApproLineInfo lineInfo,String fileName,int count){

        RequestInfo requestInfo = config.getRequestInfo();
        String appId = requestInfo.getSysHeader().getAppId();
        String orgCode = requestInfo.getSysHeader().getOrgCode();
        SysHeader sysHeader = new SysHeader(String.valueOf(new Date().getTime()), appId, UUID.randomUUID().toString().replace("-", ""), orgCode);

        BusiData busiData = new BusiData();
        busiData.setApplicationNo(lineInfo.getApplication_no());
        RequestInfo requestInfoNew = new RequestInfo(sysHeader, busiData);

        JSONObject acrsServiceData = new JSONObject();
        //ServiceData acrsServiceData = new ServiceData();
        //调用征信平台出现异常程序也不会停止

        try {
            acrsServiceData = AAOSUtil.httpCall("http://127.0.0.1/acrs/pbc/getPbcOutAppNo.do",requestInfoNew);
        } catch (Exception e){
            log.error("[ AAOS ] "+config.getBank()+" (BusinessModule) "+fileName+" row "+ count+" ACRS outAppNo Remote Call ERROR",e);
        }

        acrsServiceData = acrsServiceData.getJSONObject("busiData");
        return acrsServiceData;
    }

    private String checkAaisFields(Config config,JSONObject sourServiceData,String fileName,int count,String approMsg){

        List<String> strList = new ArrayList<>();
        for (String key:config.getAaisFileds()){
            if (FixedLenUtil.isBlank(sourServiceData.getString(key))){
                strList.add(key);
            }
        }
        if (strList.size()>0){

            StringBuffer str = new StringBuffer();
            for (String temp:strList){
                str.append(temp);
                str.append(",");
            }
            String fields = str.substring(0,str.length()-1);
            String errComment = "[ AAOS ] "+"FieldEmptyError"+" (BusinessModule) "+fileName+" row "+ count+" AAIS Remote Call ERROR, fileds:{"+fields+"} is empty "+approMsg;
            return errComment;
        } else {
            return "empty";
        }
    }

    /*
     * 校验文件中某条记录的审批字段是否为空
     * fileName 文件名  count文件中的行数  仅作为如果字段为空时，打印错误日志时定位空字段的位置
     * config 用于提供审批必输字段
     * line 文件中的行json字符串
     *
     * 如果校验出了必输字段为空，记录日志，并返回日志字符串  没有校验出必输字段为空则返回empty
     * */
    private String checkApproFileds(Config config,ApproLineInfo approLineInfo,String fileName,int count,String approMsg,String line){

        List<String> strList = new ArrayList<>();
        //将行记录转换为JSONObject对象，即map对象
        JSONObject jsonObject = JSON.parseObject(line);
        //遍历需要校验的字段
        String[] strings = config.getApproFileds();

        for (int i=0;i<strings.length;i++){
            String field = strings[i];
            //通过key(strings[i]) 获得对应value 并判断是否为空，如果是空，则放入list
            String value = jsonObject.getString(field);
            if (FixedLenUtil.isBlank(value)){
                strList.add(field);
            }
        }

        if (strList.size()>0){

            StringBuffer str = new StringBuffer();
            for (String temp:strList){
                str.append(temp);
                str.append(",");
            }
            String fields = str.substring(0,str.length()-1);
            String errComment = "[ AAOS ] "+"FieldEmptyError"+" (BusinessModule) "+fileName+" row "+count+" fields:{"+fields+"} is empty "+approMsg;
            return errComment;
        } else {
            return "empty";
        }
    }
}
